/*
 * (C) Copyright 2006-2009 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.gwt.habyt.templates.client.widgets;

import java.util.Arrays;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasName;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;

/**
 * Combo box widget
 * 
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 * 
 */
public class ComboBox extends FlowPanel implements HasText, HasName {

    protected String name;

    protected TextBox text;

    protected ListBox list;

    protected boolean dirtyList = false;

    protected PopupPanelEx popup;

    public ComboBox() {
        this(-1);
    }

    public ComboBox(int visibleTextlength) {
        DOM.setStyleAttribute(getElement(), "display", "inline-block");
        text = new TextBox();
        if (visibleTextlength > -1) {
            text.setVisibleLength(visibleTextlength);
        }
        list = new ListBox();
        Event.addNativePreviewHandler(new Event.NativePreviewHandler() {
            public void onPreviewNativeEvent(NativePreviewEvent event) {
                if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
                    popup.hide();
                }
            }
        });
        popup = new PopupPanelEx(true);
        popup.setWidget(list);
        final Button btn = new Button("...");
        btn.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                if (popup.isShowing()) {
                    popup.hide();
                } else {
                    showPopup();
                }
            }
        });
        list.addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent event) {
                text.setText(getListSelectedText());
            }
        });
        list.addKeyPressHandler(new KeyPressHandler() {
            public void onKeyPress(KeyPressEvent event) {
                int code = event.getNativeEvent().getKeyCode();
                if (code == KeyCodes.KEY_ENTER) {
                    text.setText(getListSelectedText());
                    popup.hide();
                    event.stopPropagation();
                }
            }
        });
        list.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                text.setText(getListSelectedText());
                popup.hide();
                event.stopPropagation();
            }
        });
        text.addKeyUpHandler(new KeyUpHandler() {
            public void onKeyUp(KeyUpEvent event) {
                int code = event.getNativeEvent().getKeyCode();
                if (code == KeyCodes.KEY_DOWN) {
                    showPopup();
                }
            }
        });
        text.addKeyPressHandler(new KeyPressHandler() {
            public void onKeyPress(KeyPressEvent event) {
                int code = event.getNativeEvent().getKeyCode();
                if (code == KeyCodes.KEY_ENTER) {
                    text.setSelectionRange(0, 0);
                } else if (code >= 32) {
                    sortItems();
                    int slen = text.getSelectionLength();
                    String t = text.getText();
                    if (slen > 0) {
                        t = t.substring(0, t.length() - slen);
                    }
                    t = t + (char) code;
                    String match = findFirstMatch(t);
                    if (match != null) {
                        text.setText(match);
                        text.setSelectionRange(t.length(),
                                match.length() - t.length());
                        event.preventDefault();
                    }
                }
            }
        });
        add(text);
        add(btn);
        text.addStyleName("combo-text");
        btn.addStyleName("combo-button");
        popup.setStyleName("combo-popup");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setTextLength(int length) {
        text.setVisibleLength(length);
    }

    public void showPopup() {
        showPopup(Math.min(list.getItemCount(), 10));
    }

    public void showPopup(int visibleItemsCount) {
        list.setVisibleItemCount(visibleItemsCount);
        String t = text.getText().trim();
        int last = -1;
        sortItems();
        int cnt = list.getItemCount();
        if (t.length() > 0) {
            for (int i = 0; i < cnt; i++) {
                if (list.getItemText(i).startsWith(t)) {
                    last = i;
                }
            }
        }
        if (last == -1 && cnt > 0) {
            list.setSelectedIndex(0);
        }
        popup.setWidth(getOffsetWidth() + "px");
        popup.showRelativeTo(getAbsoluteLeft(), getAbsoluteTop()
                + getOffsetHeight());
        list.setFocus(true);
    }

    protected void sortItems() {
        if (dirtyList) {
            dirtyList = false;
            int cnt = list.getItemCount();
            String[] items = new String[cnt];
            for (int i = 0; i < cnt; i++) {
                items[i] = list.getItemText(i);
            }
            list.clear();
            Arrays.sort(items);
            for (String item : items) {
                addItem(item);
            }
        }
    }

    public String getText() {
        return text.getText();
    }

    public void setText(String text) {
        this.text.setText(text);
    }

    public TextBox getTextBox() {
        return text;
    }

    public ListBox getListBox() {
        return list;
    }

    public void addItem(String text) {
        list.addItem(text);
        dirtyList = true;
    }

    public void addItem(int index, String text) {
        list.insertItem(text, index);
        dirtyList = true;
    }

    public void setItems(String[] items) {
        for (String item : items) {
            addItem(item);
        }
        dirtyList = true;
    }

    protected String findFirstMatch(String text) {
        int cnt = list.getItemCount();
        if (text.length() > 0) {
            for (int i = 0; i < cnt; i++) {
                if (list.getItemText(i).startsWith(text)) {
                    return list.getItemText(i);
                }
            }
        }
        return null;
    }

    protected String getListSelectedText() {
        int i = list.getSelectedIndex();
        if (i > -1) {
            return list.getItemText(i);
        } else {
            return null;
        }

    }

}
